﻿//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include "SynComDefs.h"
#include <shlwapi.h>
#include <psapi.h>

#include <sstream>

//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "SYNCOMLib_OCX"
#pragma resource "*.dfm"

static const UnicodeString regKey = "Software\\TwoFingerScroll";

struct TTapInfo {
	DWORD eventDown, eventUp, x;
};

static const TTapInfo tapInfo[] = {
	{0, 0, 0}, // disabled
	{MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP, 0},
	{MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP, 0},
	{MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP, 0},
	{MOUSEEVENTF_XDOWN, MOUSEEVENTF_XUP, XBUTTON1},
	{MOUSEEVENTF_XDOWN, MOUSEEVENTF_XUP, XBUTTON2}
};

TForm1 *Form1;
//---------------------------------------------------------------------------

UnicodeString __fastcall GetForegroundWindowBaseModuleName()
{
	HWND hWnd;

	hWnd = GetForegroundWindow();
	if (hWnd) {
		DWORD dwProcessId;
		HANDLE hProcess;

		GetWindowThreadProcessId(hWnd, &dwProcessId);
		hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
			PROCESS_VM_READ, false, dwProcessId);
		if (hProcess != NULL) {
			HMODULE hMod;
			DWORD cbNeeded;

			if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) {
				char szModule[MAX_PATH];

				GetModuleBaseName(hProcess, hMod, szModule, MAX_PATH);
				CloseHandle(hProcess);
				return UnicodeString(szModule);
			}

			CloseHandle(hProcess);
		}
	}

	return UnicodeString();
}
//---------------------------------------------------------------------------

int __fastcall GetScrollMode()
{
	UnicodeString name = GetForegroundWindowBaseModuleName();
	int mode = 1;

	if (name != "") {
		TRegistry *reg;

		reg = new TRegistry();
		reg->RootKey = HKEY_CURRENT_USER;
		if (reg->OpenKeyReadOnly(regKey + "\\scrollModeApps")) {
			if (reg->ValueExists(name)) {
				mode = reg->ReadInteger(name);
			}
			reg->CloseKey();
		}
		delete reg;
	}
	return mode;
}
//---------------------------------------------------------------------------

void __fastcall SetScrollMode(int mode)
{
	UnicodeString name = GetForegroundWindowBaseModuleName();

	if (name != "") {
		TRegistry *reg;

		reg = new TRegistry();
		reg->RootKey = HKEY_CURRENT_USER;
		reg->Access = KEY_ALL_ACCESS;
		if (reg->OpenKey(regKey + "\\scrollModeApps", true)) {
			if (mode != 1) {
				reg->WriteInteger(name, mode);
			}
			else {
				reg->DeleteValue(name);
            }
			reg->CloseKey();
		}
		delete reg;
	}
}
//---------------------------------------------------------------------------

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
	synAPI = new TSynAPI(this);
	synTouchPad = new TSynDevice(this);

	synAPI->Initialize();

	long devHandle = -1;

	synAPI->FindDevice(SE_ConnectionAny,
		SE_DeviceTouchPad,
		&devHandle);

	if (devHandle < 0) {
		Application->MessageBox(L"No Synaptics TouchPad device found!",
			L"TwoFingerScroll");
		Application->Terminate();
	}

	synTouchPad->Select(devHandle);
	synTouchPad->CreatePacket(&synPacket);

	long multi;

	synTouchPad->GetProperty(SP_IsMultiFingerCapable, &multi);
	if (!multi) {
		TRegistry *reg = new TRegistry();
		reg->RootKey = HKEY_LOCAL_MACHINE;
		reg->Access = KEY_ALL_ACCESS;
		if (!reg->OpenKey("System\\CurrentControlSet\\Services\\SynTP\\Parameters", false)) {
			Application->MessageBox(L"Synaptics kernel driver registry keys missing. Reinstall drivers.",
				L"TwoFingerScroll");
			Application->Terminate();
		}
		unsigned int mask = 0;
		if (reg->ValueExists("CapabilitiesMask")) {
			mask = reg->ReadInteger("CapabilitiesMask");
			if (mask == 0xFFFFFFFF) {
				Application->MessageBox(L"Driver support for multiple fingers is already enabled but\n"
					"the driver still doesn't report multiple fingers. Either you haven't restarted\n"
					"the system yet or your TouchPad doesn't support multiple fingers.",
					L"TwoFingerScroll");
				Application->Terminate();
			}
		}
		if (mask != 0xFFFFFFFF) {
			reg->WriteInteger("CapabilitiesMask", 0xFFFFFFFF);
			reg->CloseKey();
			Application->MessageBox(L"Driver support for multiple fingers has been enabled.\n"
            	"\n"
				"Restart system for the changes to take effect.",
				L"TwoFingerScroll");
			Application->Terminate();
		}
		reg->CloseKey();
	}

	// activate the OnPacket event
	synTouchPad->SetSynchronousNotification(this);

	settings = new TRegistry();

	SettingsLoad();

}
//---------------------------------------------------------------------------

void __fastcall TForm1::SettingsLoad(bool defaults)
{
	settings->RootKey = HKEY_CURRENT_USER;
	if (!defaults)
		settings->OpenKeyReadOnly(regKey);

	if (settings->ValueExists("scrollTwoFinger")) {
		EnableTwoFingerScrollCheckBox->Checked = settings->ReadBool("scrollTwoFinger");
	} else {
		EnableTwoFingerScrollCheckBox->Checked = true;
	}

	if (settings->ValueExists("scrollThreeFinger")) {
		EnableThreeFingerScrollCheckBox->Checked = settings->ReadBool("scrollThreeFinger");
	} else {
		EnableThreeFingerScrollCheckBox->Checked = false;
	}

	RefreshScrollSettings();

	if (settings->ValueExists("scrollHorizontal")) {
		scrollHorizontalCheckBox->Checked = settings->ReadBool("scrollHorizontal");
	} else {
		scrollHorizontalCheckBox->Checked = true;
	}

	if (settings->ValueExists("preventMouseSkip")) {
		preventMouseSkipCheckBox->Checked = settings->ReadBool("preventMouseSkip");
	} else {
		preventMouseSkipCheckBox->Checked = true;
	}

	if (settings->ValueExists("enableDeadzone")) {
		deadzoneCheckBox->Checked = settings->ReadBool("enableDeadzone");
	} else {
		deadzoneCheckBox->Checked = true;
	}

	if (settings->ValueExists("scrollLinearEdge")) {
		scrollLinearEdge->Checked = settings->ReadInteger("scrollLinearEdge");
	} else {
		scrollLinearEdge->Checked = true;
	}

	if (settings->ValueExists("scrollSpeed")) {
		scrollSpeed->Position =
			settings->ReadInteger("scrollSpeed");
	}
	else scrollSpeed->Position = 100;

	if (settings->ValueExists("scrollAccEnabled")) {
		scrollAccEnabled->Checked =
			settings->ReadInteger("scrollAccEnabled");
	}
	else scrollAccEnabled->Checked = true;
	scrollAccEnabledClick(NULL);

	if (settings->ValueExists("scrollAcc")) {
		scrollAcc->Position =
			settings->ReadInteger("scrollAcc");
	}
	else scrollAcc->Position = 75;

	if (settings->ValueExists("scrollMode")) {
		switch (settings->ReadInteger("scrollMode")) {
		case 1:
			scrollSmooth->Checked = true;
			break;
		case 2:
			scrollSmart->Checked = true;
			break;
		default:
			scrollCompatible->Checked = true;
		}
	}
	else scrollSmooth->Checked = true;

	if (settings->ValueExists("tapOneOne")) {
		tapOneOne->ItemIndex =
			settings->ReadInteger("tapOneOne");
	}
	else tapOneOne->ItemIndex = 0;

	if (settings->ValueExists("tapTwo")) {
		tapTwo->ItemIndex =
			settings->ReadInteger("tapTwo");
	}
	else tapTwo->ItemIndex = 0;

	if (settings->ValueExists("tapTwoOne")) {
		tapTwoOne->ItemIndex =
			settings->ReadInteger("tapTwoOne");
	}
	else tapTwoOne->ItemIndex = 0;

	if (settings->ValueExists("tapThree")) {
		tapThree->ItemIndex =
			settings->ReadInteger("tapThree");
	}
	else tapThree->ItemIndex = 0;

	if (settings->ValueExists("tapMaxDistance")) {
		tapMaxDistance->Position =
			settings->ReadInteger("tapMaxDistance");
	}
	else tapMaxDistance->Position = 50;

	settings->CloseKey();

	TRegistry *reg = new TRegistry();
	reg->RootKey = HKEY_CURRENT_USER;
	if (!defaults)
		reg->OpenKeyReadOnly("Software\\Microsoft\\Windows\\CurrentVersion\\Run");

	if (reg->ValueExists("TwoFingerScroll")) {
		 startWithWindows->Checked = (reg->ReadString("TwoFingerScroll") ==
			Application->ExeName);
	}
	else startWithWindows->Checked = false;

	reg->CloseKey();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::SettingsSave()
{
	settings->RootKey = HKEY_CURRENT_USER;
	settings->Access = KEY_ALL_ACCESS;
	if (!settings->OpenKey(regKey, true))
		return;

	settings->WriteBool("scrollTwoFinger", EnableTwoFingerScrollCheckBox->Checked);
	settings->WriteBool("scrollThreeFinger", EnableThreeFingerScrollCheckBox->Checked);
	settings->WriteBool("scrollHorizontal", scrollHorizontalCheckBox->Checked);
	settings->WriteBool("enableDeadzone", deadzoneCheckBox->Checked);
	settings->WriteBool("preventMouseSkip", preventMouseSkipCheckBox->Checked);

	settings->WriteInteger("scrollLinearEdge", scrollLinearEdge->Checked);
	settings->WriteInteger("scrollSpeed", scrollSpeed->Position);
	settings->WriteInteger("scrollAccEnabled", scrollAccEnabled->Checked);
	settings->WriteInteger("scrollAcc", scrollAcc->Position);
	settings->WriteInteger("scrollMode", scrollSmooth->Checked ? 1 : (scrollSmart->Checked ? 2 : 0));
	settings->WriteInteger("tapOneOne", tapOneOne->ItemIndex);
	settings->WriteInteger("tapTwo", tapTwo->ItemIndex);
	settings->WriteInteger("tapTwoOne", tapTwoOne->ItemIndex);
	settings->WriteInteger("tapThree", tapThree->ItemIndex);
	settings->WriteInteger("tapMaxDistance", tapMaxDistance->Position);

	settings->CloseKey();

	TRegistry *reg = new TRegistry();
	reg->RootKey = HKEY_CURRENT_USER;
	reg->Access = KEY_ALL_ACCESS;
	reg->OpenKey("Software\\Microsoft\\Windows\\CurrentVersion\\Run", false);

	if (startWithWindows->Checked) {
		reg->WriteString("TwoFingerScroll", Application->ExeName);
	}
	else if (reg->ValueExists("TwoFingerScroll")) {
		reg->DeleteValue("TwoFingerScroll");
	}

	reg->CloseKey();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::AcquirePad(bool acquire, bool onlyMotion)
{
	if (acquire && !fIsPadAcquired)
	{
		synTouchPad->Acquire(onlyMotion ? SF_AcquireMotionOnly : SF_AcquireAll);
	}
	else if (!acquire && fIsPadAcquired)
	{
		synTouchPad->Unacquire();
	}
	fIsPadAcquired = acquire;
}

//---------------------------------------------------------------------------

void __fastcall TForm1::LockDeviceTap(bool lock)
{
	long gest;

	if (lock != fIsDeviceTapLocked)
	{
		if (lock)
		{
			synTouchPad->GetProperty(SP_Gestures, &gest);
			if (gest & SF_GestureTap)
			{
				synTouchPad->SetProperty(SP_Gestures, gest & (~SF_GestureTap));
				fSynTapState = true;
			}
			else
			{
				fSynTapState = false;
            }
		}
		else
		{
			synTouchPad->GetProperty(SP_Gestures, &gest);
			if (fSynTapState)
			{
				gest |= SF_GestureTap;
			}
			else
			{
				gest &= ~SF_GestureTap;
            }
			synTouchPad->SetProperty(SP_Gestures, gest);
		}
		fIsDeviceTapLocked = lock;
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Settings1Click(TObject *Sender)
{
	pages->ActivePageIndex = 0;
	Show();
	ok->SetFocus();
	SetForegroundWindow(Handle);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
	Hide();
	SettingsLoad();
	Action = caNone;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Exit1Click(TObject *Sender)
{
	Application->Terminate();
}
//---------------------------------------------------------------------------

HRESULT STDMETHODCALLTYPE TForm1::OnSynDevicePacket(long seqNum)
{
	// if its not enabled, return
	if (!globalActive->Checked)
		return 0;

	long numberOfFingers;
	long currentNumberOfFingers;
	long fingerState;
	long deltaX;
	long deltaY;
	long buttonState;
	long y;
	long timestamp;

	// get the pointing data packet
	synTouchPad->LoadPacket(synPacket);

	// extract relevant data
	synPacket->GetProperty(SP_ButtonState, &buttonState);
	// SP_ExtraFingerState is undocumented but returns the number of fingers, 0-3
	synPacket->GetProperty(SP_ExtraFingerState, &numberOfFingers);
	synPacket->GetProperty(SP_FingerState, &fingerState);
	synPacket->GetProperty(SP_XDelta, &deltaX);
	synPacket->GetProperty(SP_YDelta, &deltaY);
	synPacket->GetProperty(SP_Y, &y);
	synPacket->GetProperty(SP_TimeStamp, &timestamp);

	// the right 3 bits represent the number of fingers on the trackpad (up to max 3)
	currentNumberOfFingers = numberOfFingers & 3;

	if (currentNumberOfFingers == 0) {
		fNoFingersTimestamp = timestamp;
	}

	// check if number of fingers on trackpad have changed
	if (currentNumberOfFingers != fLastNumberOfFingers) {

		/*
		if previous state was zero fingers, this is the initial num of
		fingers or if time since zero fingers is really small then this is
		also the initial num of fingers (in case the fingers do not hit the
		touchpad at exactly the same time)
		*/
		if (fLastNumberOfFingers == 0 || (timestamp - fNoFingersTimestamp) < 100) {
			fInitialNumberOfFingers = currentNumberOfFingers;
		}

		// store current number of fingers and timestamp
		fLastNumberOfFingers = currentNumberOfFingers;
		fLastNumberOfFingersTimestamp = timestamp;
	}

	// the left 3 bits represent the number of fingers that were initially on the trackpad
	// initialNumberOfFingers = numberOfFingers >> 8 & 3;

	// if the user is clicking the button, don't do anything
	if (buttonState & SF_ButtonAnyPhysical) {
		AcquirePad(false);
		return 0;
	}

	if (preventMouseSkipCheckBox->Checked) {
		if (currentNumberOfFingers == 2 && fInitialNumberOfFingers == 1) {
			AcquirePad(true, true);
			return 0;
		} else {
			AcquirePad(false);
		}
	}

	if (deadzoneCheckBox->Checked) {
		if (y < 1700 && currentNumberOfFingers == 1) {
			AcquirePad(true, true);
			return 0;
		} else {
			AcquirePad(false);
		}
	}

	// handle tapping
	bool wasTapPacket = handleTapPacket (
		currentNumberOfFingers,
		deltaX,
		deltaY,
		fingerState
	);

	if (wasTapPacket) {
		return 0;
	}

	// handle scrolling
	bool wasScrollPacket = handleScrollPacket(
		currentNumberOfFingers,
		deltaX,
		deltaY,
		fingerState
	);

	if (wasScrollPacket) {
    	return wasScrollPacket;
	}

	return 0;
}

//---------------------------------------------------------------------------

bool __fastcall TForm1::handleScrollPacket
(
	long currentNumberOfFingers,
	long deltaX,
	long deltaY,
	long fingerState
)
{
	if (EnableTwoFingerScrollCheckBox->Checked || EnableThreeFingerScrollCheckBox->Checked) {
		//if (fingerState & SF_FingerPresent && fInitialNumberOfFingers == 2 && currentNumberOfFingers == 2) {
		if 		((EnableTwoFingerScrollCheckBox->Checked &&
				fInitialNumberOfFingers == 2 &&
				currentNumberOfFingers == 2)
				||
				(EnableThreeFingerScrollCheckBox->Checked &&
				fInitialNumberOfFingers == 3 &&
				currentNumberOfFingers == 3)) {

			if (fScrollTouchTimestamp == 0) {
				GetCursorPos(&fScrollTouchPos);
				synPacket->GetProperty(SP_TimeStamp, &fScrollTouchTimestamp);
			}
			//if (fInitialNumberOfFingers == 2 && currentNumberOfFingers == 2) {
				bool wasScroll = handleTwoFingerScroll
				(
					currentNumberOfFingers,
					deltaX,
					deltaY,
					fingerState
				);

				if (wasScroll) {
					return wasScroll;
				}
//			} else {
//				fScrollLastXDelta = fScrollLastYDelta = 0;
//				AcquirePad(false);
//				fScrollBuffer = 0;
//				fScrollNotEdge = false;
//			}
		} else { // finger is no longer present
			fScrollTouchTimestamp = 0;
			fScrollLastXDelta = fScrollLastYDelta = 0;
			AcquirePad(false);
			fScrollBuffer = 0;
			fScrollNotEdge = false;
		}
	}

	return false;
}

//---------------------------------------------------------------------------

bool __fastcall TForm1::handleTwoFingerScroll
(
	long currentNumberOfFingers,
	long deltaX,
	long deltaY,
	long fingerState
)
{
	long y;
	long min_possible_y;
	long max_possible_y;
	synPacket->GetProperty(SP_Y, &y);
	synTouchPad->GetProperty(SP_YLoBorder, &min_possible_y);
	synTouchPad->GetProperty(SP_YHiBorder, &max_possible_y);

	// perform edge scroll
	if (fIsPadAcquired && scrollLinearEdge->Checked) {
		if (y >= min_possible_y && y <= max_possible_y) {
			fScrollNotEdge = true;
		} else if
		(
			fScrollNotEdge
			&&
			(
				(y < min_possible_y && fScrollLastYDelta < 0)
				||
				(y > max_possible_y && fScrollLastYDelta > 0)
			)
		)
		{
			DoScroll(fScrollLastXDelta, fScrollLastYDelta);

			return true;
		}
	}

	// if the finger is in motion or initial/current number of fingers are present
	if (fingerState & SF_FingerMotion ||

				(EnableTwoFingerScrollCheckBox->Checked &&
				fInitialNumberOfFingers == 2 &&
				currentNumberOfFingers == 2)
				||
				(EnableThreeFingerScrollCheckBox->Checked &&
				fInitialNumberOfFingers == 3 &&
				currentNumberOfFingers == 3)) {

		if (!fIsPadAcquired) {
			AcquirePad(true);

			// Causes mouse to skip on Envy 14
			/*
			long tstamp;
			synPacket->GetProperty(SP_TimeStamp, &tstamp);
			if (tstamp - fScrollTouchTime < 1000) {
				SetCursorPos(fScrollTouchPos.x,
					fScrollTouchPos.y);
			}
			*/

			if (scrollCompatible->Checked) {
				fScrollMode = 0;
			} else if (scrollSmooth->Checked) {
				fScrollMode = 1;
			} else {
				fScrollMode = GetScrollMode();
				if
				(
					(GetKeyState(VK_SHIFT) & 0x8000)
					&&
					(GetKeyState(VK_CONTROL) & 0x8000)
					&&
					(GetKeyState(VK_MENU) & 0x8000)
				)
				{
					// toggle scroll mode
					if (fScrollMode == 1) {
						fScrollMode = 0;
					} else {
						fScrollMode = 1;
                    }
					SetScrollMode(fScrollMode);
				}
			}
		}
		if (fIsPadAcquired) {
			DoScroll(deltaX, deltaY);
			fScrollLastXDelta = deltaX;
			fScrollLastYDelta = deltaY;

			return true;
		}
	}

	return false;
}

//---------------------------------------------------------------------------

bool __fastcall TForm1::handleTapPacket
(
	long currentNumberOfFingers,
	long deltaX,
	long deltaY,
	long fingerState
)
{
	// if more fingers are down than previously, its an interesting event
	// 1) first time the pad was clicked (previous number of fingers = 0)
	// 2) the second tap of a two stage sequence of taps (1->1, 2->1)
	if (currentNumberOfFingers > fTapLastNumberOfFingers) {
		if (currentNumberOfFingers >= 2) {
			synPacket->GetProperty(SP_TimeStamp, &fTapStartTime);
			fTapDistance = 0;
			LockDeviceTap(true);
		}
		if (fTapLastNumberOfFingers == 0) {
			synPacket->GetProperty(SP_TimeStamp, &fTapTouchTime);
			GetCursorPos(&fTapTouchPos);
		}
	}
	// the user has lifted some fingers finishing the tap sequence
	else if (currentNumberOfFingers < fTapLastNumberOfFingers)
	{
		if
		(
			(fTapDistance < tapMaxDistance->Position)
			&&
			(fTapLastNumberOfFingers >= 2)
		)
		{
			bool ok = false;
			long tstamp;
			synPacket->GetProperty(SP_TimeStamp, &tstamp);
			// if this single tap is less than 175ms
			if (tstamp - fTapTouchTime < 175) {
				if (fTapLastNumberOfFingers == 2) {
					ok = DoTap(tapTwo->ItemIndex);
				}
				else if (fTapLastNumberOfFingers == 3) {
					ok = DoTap(tapThree->ItemIndex);
				}
			}
			// if the time since the last tap to release is less than 175ms
			else if (tstamp - fTapStartTime < 175) {
//				std::stringstream ss;
//				ss << fTapLastNumberOfFingersSequence1 << "->" << fTapLastNumberOfFingersSequence2;
//				MessageBox(NULL, ss.str().c_str(), ss.str().c_str(), MB_OK);
				if (fTapLastNumberOfFingers == 2) {
					ok = DoTap(tapOneOne->ItemIndex);
				} else if (fTapLastNumberOfFingers == 3) {
					ok = DoTap(tapTwoOne->ItemIndex);
				}
			}

			if (ok) {
				SetCursorPos(fTapTouchPos.x, fTapTouchPos.y);
			}

			// prevent tap trigger until initiated again
			fTapStartTime -= 175;
			fTapLastNumberOfFingers = currentNumberOfFingers;

			return true;
		}
	}
	if (fIsDeviceTapLocked)
	{
		if (abs(deltaX) < 800) {
			fTapDistance += abs(deltaX);
		}
		if (abs(deltaY) < 800) {
			fTapDistance += abs(deltaY);
		}
		if (!(fingerState & SF_FingerPresent)) {
			LockDeviceTap(false);
		}
	}

	fTapLastNumberOfFingers = currentNumberOfFingers;

	return false;
}

//---------------------------------------------------------------------------

bool __fastcall TForm1::DoTap(int index)
{
	INPUT i[2];
	const TTapInfo *info = &tapInfo[index];

	if (info->eventDown == 0) return false; // tapping disabled

	ZeroMemory(i, sizeof(INPUT)*2);
	i[0].type = INPUT_MOUSE;
	i[0].mi.dwFlags = info->eventDown;
	i[0].mi.mouseData = info->x;
	i[1].type = INPUT_MOUSE;
	i[1].mi.dwFlags = info->eventUp;
	i[1].mi.mouseData = info->x;
	SendInput(2, i, sizeof(INPUT));

	return true;
}
//---------------------------------------------------------------------------

bool __fastcall TForm1::DoScroll(long deltaX, long deltaY)
{
	if (abs(deltaX) > 800 || abs(deltaY) > 800) {
		return false;
	}

	if (scrollHorizontalCheckBox->Checked) {
		if (abs(deltaX) > 5) {
			DoHorizontalScroll(deltaX);
		}
	}

	if (abs(deltaY) > 5) {
		DoVerticalScroll(deltaY);
	}

//	if (abs(deltaX) > abs(deltaY)) {
//		DoHorizontalScroll(deltaX);
//	} else {
//		DoVerticalScroll(deltaY);
//	}

	return true;
}

// If dwFlags contains MOUSEEVENTF_HWHEEL, then dwData specifies the amount of wheel movement.
// A positive value indicates that the wheel was rotated to the right;
// a negative value indicates that the wheel was rotated to the left.
void __fastcall TForm1::DoHorizontalScroll(long deltaX)
{
	long d;

	// scrollSpeed
	deltaX = deltaX * scrollSpeed->Position / 100;

	// scrollAcc
	if (scrollAccEnabled->Checked) {
		d = deltaX * deltaX / (scrollAcc->Max - scrollAcc->Position + scrollAcc->Min);
		if (d < 4)
		{
			d = 4;
		}
		if (deltaX < 0)
		{
			d = -d;
		}
	} else {
		d = deltaX;
    }

	if (fScrollMode == 0) {
		// compatibility mode
		fScrollBuffer += d;
		d = fScrollBuffer - fScrollBuffer % WHEEL_DELTA;
	}

	if (d != 0) {
		INPUT i;

		ZeroMemory(&i, sizeof(INPUT));
		i.type = INPUT_MOUSE;
		i.mi.dwFlags = MOUSEEVENTF_HWHEEL;
		i.mi.mouseData = d;
		SendInput(1, &i, sizeof(INPUT));

		if (fScrollMode == 0) { // compatibility mode
			fScrollBuffer -= d;
        }
	}
}

// If dwFlags contains MOUSEEVENTF_WHEEL, then dwData specifies the amount of wheel movement.
// A positive value indicates that the wheel was rotated forward, away from the user;
// a negative value indicates that the wheel was rotated backward, toward the user.
// One wheel click is defined as WHEEL_DELTA, which is 120.
void __fastcall TForm1::DoVerticalScroll(long deltaY)
{
	long d;

	// scrollSpeed
	deltaY = deltaY * scrollSpeed->Position / 100;

	// scrollAcc
	if (scrollAccEnabled->Checked) {
		d = deltaY * deltaY / (scrollAcc->Max - scrollAcc->Position + scrollAcc->Min);
		if (d < 4) {
			d = 4;
		}
		if (deltaY < 0) {
			d = -d;
		}
	} else {
		d = deltaY;
	}

	if (fScrollMode == 0) {
		// compatibility mode
		fScrollBuffer += d;
		d = fScrollBuffer - fScrollBuffer % WHEEL_DELTA;
	}

	if (d != 0) {
		INPUT i;

		ZeroMemory(&i, sizeof(INPUT));
		i.type = INPUT_MOUSE;
		i.mi.dwFlags = MOUSEEVENTF_WHEEL;
		i.mi.mouseData = d;
		SendInput(1, &i, sizeof(INPUT));

		if (fScrollMode == 0) { // compatibility mode
			fScrollBuffer -= d;
		}
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::okClick(TObject *Sender)
{
	Hide();
	SettingsSave();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::cancelClick(TObject *Sender)
{
	Hide();
	SettingsLoad();
}
//---------------------------------------------------------------------------

void __fastcall TForm1::About1Click(TObject *Sender)
{
	Application->MessageBox(L"TwoFingerScroll for HP Envy 14 v0.02α\n(based on TwoFingerScroll v1.0.6)\n"
		"\n"
		"Published under the Apache 2.0 License.\n"
		"\n"
		"Project home:\n"
		"http://code.google.com/p/envy14-trackpad-app\n",
		L"About...");
}
//---------------------------------------------------------------------------

void __fastcall TForm1::defaultsClick(TObject *Sender)
{
	SettingsLoad(true);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Dispatch(void *Message)
{
	// the Synaptics API seems to have a problem with
	// power state changes; in particullar, the events
	// get deactivated after standby or hibernation;
	// here we ensure that they stay activated

	if (((TMessage *)Message)->Msg == WM_POWERBROADCAST) {
		reactivateTimer->Tag = 20;
		reactivateTimer->Enabled = true;
	}

	TForm::Dispatch(Message);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::reactivateTimerTimer(TObject *Sender)
{
	synTouchPad->SetSynchronousNotification(this);
	reactivateTimer->Tag--;
	if (!reactivateTimer->Tag) {
		reactivateTimer->Enabled = false;
	}
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Label1Click(TObject *Sender)
{
	Application->MessageBox(
		L"Compatible:\n"
		"The scroll closely simulates a mouse scroll wheel. This mode works with\n"
		"most applications.\n"
		"\n"
		"Smooth:\n"
		"Smooth scrolling. Some older applications may not work propery in this\n"
		"scroll mode.\n"
		"\n"
		"Smart:\n"
		"Uses smooth scrolling by default. Compatible mode can be enabled for\n"
		"specifc applications by scrolling within them while keeping SHIFT, CTRL\n"
		"and ALT keys pressed down. The setting is remembered, all future scrolls\n"
		"in the same application will use the compatible mode. Scrolling with the\n"
		"keys again reverts back to the smooth mode.",
		L"Scroll mode");
}
//---------------------------------------------------------------------------

void __fastcall TForm1::scrollAccEnabledClick(TObject *Sender)
{
	scrollAcc->Enabled = scrollAccEnabled->Checked;
}
//---------------------------------------------------------------------------

void __fastcall TForm1::EnableTwoFingerScrollCheckBoxClick(TObject *Sender)
{
	RefreshScrollSettings();
}

//---------------------------------------------------------------------------

void __fastcall TForm1::EnableThreeFingerScrollCheckBoxClick(TObject *Sender)
{
	RefreshScrollSettings();
}

//---------------------------------------------------------------------------

void __fastcall TForm1::RefreshScrollSettings() {
	if (EnableTwoFingerScrollCheckBox->Checked || EnableThreeFingerScrollCheckBox->Checked) {
		ToggleScrollSettingsEnabled(true);
	} else {
		ToggleScrollSettingsEnabled(false);
	}

}

//---------------------------------------------------------------------------

void __fastcall TForm1::ToggleScrollSettingsEnabled(bool e) {
	scrollLinearEdge->Enabled = e;
	scrollSpeedLabel->Enabled = e;
	scrollSpeed->Enabled = e;
	scrollAccEnabled->Enabled = e;
	scrollAcc->Enabled = e;
	scrollCompatible->Enabled = e;
	scrollSmooth->Enabled = e;
	scrollSmart->Enabled = e;
	scrollHorizontalCheckBox->Enabled = e;
}
//---------------------------------------------------------------------------


